/**
 * 
 */
package business.product;

import java.util.List;

import javax.servlet.http.HttpServletRequest;

import security.SecurityRole;
import service.Service;
import service.Service.ServiceType;
import service.ServiceManager;
import service.resources.Dao;
import bean.Component;
import business.exception.ComponentCreationException;
import business.exception.ComponentSearchException;
import business.exception.ComponentUpdateException;
import data.DaoFactory.DaoName;
import data.IComponentDao;
import data.Pagination;
import data.exception.DaoException;

/**
 * Component Inventory.
 * 
 * @author chuxiaoyuan
 * 
 */
public class ComponentInventory extends ServiceManager {

	static {
		REGISTER(ComponentInventory.class);
	}

	/**
	 * Constructor.
	 * 
	 * @param request
	 *            the HTTP servlet request.
	 */
	public ComponentInventory(HttpServletRequest request) {
		super(request);
	}

	/**
	 * The Component DAO.
	 */
	@Dao(name = DaoName.ComponentDao)
	private IComponentDao componentDao;

	/**
	 * Add new Component.
	 */
	public static final String SERVICE_NEW_COMPONENT = "NEW_COMPONENT";

	/**
	 * Update Component Information (not including the component quantity).
	 */
	public static final String SERVICE_UPDATE_COMPONENT_INFO = "UPDATE_COMPONENT_INFO";

	/**
	 * Consume in stock component which makes the quantity being reduced.
	 */
	public static final String SERVICE_CONSUME_COMPONENT = "CONSUME_COMPONENT";

	/**
	 * Supply components to add to the quantity.
	 */
	public static final String SERVICE_SUPPLY_COMPONENT = "SUPPLY_COMPONENT";

	/**
	 * Get Component information by the compoent id.
	 */
	public static final String SERVICE_GET_COMPONENT_BYID = "GET_COMPONENT_BYID";

	/**
	 * Search Component by key words.
	 */
	public static final String SERVICE_SEARCH_COMPONENT = "SEARCH_COMPONENT";

	/**
	 * Search Component by key words with pagination.
	 */
	public static final String SERVICE_SEARCH_COMPONENT_WITH_PAGINATION = "SEARCH_COMPONENT_WITH_PAGINATION";

	/**
	 * Find all Components.
	 */
	public static final String SERVICE_FIND_ALL_COMPONENTS = "FIND_ALL_COMPONENTS";

	/**
	 * Find all Components with pagination.
	 */
	public static final String SERVICE_FIND_ALL_COMPONENTS_WITH_PAGINATION = "FIND_ALL_COMPONENTS_WITH_PAGINATION";

	/**
	 * Add new component to inventory.
	 * 
	 * @return the added new component. Auto-generated keys will be populated.
	 */
	@Service(name = SERVICE_NEW_COMPONENT, type = ServiceType.Transaction, security = SecurityRole.Admin)
	public Component newComponent(Component component)
			throws ComponentCreationException {

		try {
			// Insert new component
			componentDao.insert(component);
			return component;

		} catch (DaoException e) {
			String msg = "Failed to create new component.";
			throw new ComponentCreationException(msg, e);
		}
	}

	/**
	 * Update component information (not including quantity).
	 * 
	 * @return the affected rows.
	 */
	@Service(name = SERVICE_UPDATE_COMPONENT_INFO, type = ServiceType.Transaction, security = SecurityRole.Admin)
	public int updateComponentInfo(Component component)
			throws ComponentUpdateException {

		try {
			// Update component information
			return componentDao.updateComponentInfo(component);

		} catch (DaoException e) {
			String msg = "Failed to update component information.";
			throw new ComponentUpdateException(msg, e);
		}
	}

	/**
	 * Consume in stock component which makes the quantity being reduced.
	 * 
	 * @return the updated component
	 */
	@Service(name = SERVICE_CONSUME_COMPONENT, type = ServiceType.Transaction, security = SecurityRole.Admin)
	public Component consumeComponent(long componentId, long quantity)
			throws ComponentUpdateException {

		if (quantity <= 0) {
			String msg = "Invalid quantity input for supplyComponent: "
					+ quantity;
			throw new ComponentUpdateException(msg);
		}

		try {
			// Decrease quantity.
			if (componentDao.decreaseComponentQuantity(componentId, quantity) != 1) {
				String msg = "Invalid component id: " + componentId;
				throw new ComponentUpdateException(msg);
			}

			// Get the updated component
			Component component = componentDao.selectById(componentId);

			if (component.getQuantity() < 0) {
				String msg = "Quantity must be greater than 0 for component: "
						+ componentId;
				throw new ComponentUpdateException(msg);
			}
			return component;

		} catch (DaoException e) {
			String msg = "Comsume component failed.";
			throw new ComponentUpdateException(msg, e);
		}
	}

	/**
	 * Supply components to add to the quantity.
	 * 
	 * @return the updated component
	 */
	@Service(name = SERVICE_SUPPLY_COMPONENT, type = ServiceType.Transaction, security = SecurityRole.Admin)
	public Component supplyComponent(long componentId, long quantity)
			throws ComponentUpdateException {

		if (quantity <= 0) {
			String msg = "Invalid quantity input for supplyComponent: "
					+ quantity;
			throw new ComponentUpdateException(msg);
		}

		try {
			// Increase quantity.
			if (componentDao.increaseComponentQuantity(componentId, quantity) != 1) {
				String msg = "Invalid component id: " + componentId;
				throw new ComponentUpdateException(msg);
			}

			// Get the updated component
			Component component = componentDao.selectById(componentId);
			return component;

		} catch (DaoException e) {
			String msg = "Comsume component failed.";
			throw new ComponentUpdateException(msg, e);
		}
	}

	/**
	 * Get component by Id.
	 * 
	 * @return The component or null if not found.
	 */
	@Service(name = SERVICE_GET_COMPONENT_BYID, type = ServiceType.Query, security = SecurityRole.Admin)
	public Component getComponentById(long id) throws ComponentSearchException {

		try {
			// Select by Id.
			return componentDao.selectById(id);

		} catch (DaoException e) {
			String msg = "Failed to select component by id: " + id;
			throw new ComponentSearchException(msg, e);
		}
	}

	/**
	 * Search component by keyword.
	 * 
	 * @return The component or null if not found.
	 */
	@Service(name = SERVICE_SEARCH_COMPONENT, type = ServiceType.Query, security = SecurityRole.Admin)
	public List<Component> searchComponents(String keyword)
			throws ComponentSearchException {

		// Search without pagination.
		return this.searchComponents(keyword, null);
	}

	/**
	 * Search component by keyword with pagination.
	 * 
	 * @return The component or null if not found.
	 */
	@Service(name = SERVICE_SEARCH_COMPONENT_WITH_PAGINATION, type = ServiceType.Query, security = SecurityRole.Admin)
	public List<Component> searchComponents(String keyword,
			Pagination pagination) throws ComponentSearchException {

		// Validate input.
		if (keyword == null) {
			throw new ComponentSearchException(
					"Invalid keyword: keyword cannot be null!");
		}
		keyword = keyword.trim();
		if ("".equals(keyword)) {
			throw new ComponentSearchException(
					"Invalid keyword: keyword cannot be empty!");
		}

		try {
			// Select by keywords
			if (pagination == null) {

				return componentDao.selectByKeywords(keyword);
			} else {

				return componentDao.selectByKeywords(keyword, pagination);
			}

		} catch (DaoException e) {
			String msg = "Failed to search components by keyword: " + keyword;
			throw new ComponentSearchException(msg, e);
		}
	}

	/**
	 * Find all components with pagination.
	 * 
	 * @return The component or null if not found.
	 */
	@Service(name = SERVICE_FIND_ALL_COMPONENTS_WITH_PAGINATION, type = ServiceType.Query, security = SecurityRole.Admin)
	public List<Component> findAllComponents(Pagination pagination)
			throws ComponentSearchException {

		try {
			// Select all
			if (pagination == null) {

				return componentDao.selectAll();
			} else {

				return componentDao.selectAll(pagination);
			}

		} catch (DaoException e) {
			String msg = "Failed to find all components";
			throw new ComponentSearchException(msg, e);
		}
	}

	/**
	 * Find all components with no pagination.
	 * 
	 * @return The component or null if not found.
	 */
	@Service(name = SERVICE_FIND_ALL_COMPONENTS, type = ServiceType.Query, security = SecurityRole.Admin)
	public List<Component> findAllComponents() throws ComponentSearchException {

		// No pagination
		return this.findAllComponents(null);
	}
}
